<?php
/**
 * Nweb Framework
 *
 * This script is protected by copyright. It's use, copying, modification
 * and distribution without written consent of the author is prohibited.
 *
 * @category    source
 * @package     pl.nweb.framework.application
 * @subpackage  dispatcher
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt  GNU Lesser General Public
 * @version     3.0 $Id$
 * @link        http://code.google.com/p/nweb-framework/
 * @link        http://framework.nweb.pl
 */

namespace pl\nweb\framework\application;

/**
 * Dispatcher
 *
 * @category    source
 * @package     pl.nweb.framework.application
 * @subpackage  dispatcher
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @version     3.0 $Id$
 */
class Dispatcher implements dispatcher\iDispatcher
{
    /**
     * Domyślne ustawienia
     *
     * @var array
     */
    private $_defaults = array(
        self::OPTION_DEFAULT_MODULE     => 'home',
        self::OPTION_DEFAULT_CONTROLLER => 'index',
        self::OPTION_STATIC_ACTION      => 'index'
    );

    /**
     * Domyślne ustawienia
     *
     * @var array
     */
    private $_options = array();

    /**
     * Opcje
     */
    const OPTION_APPLICATION_NAMESPACE = 'application.namespace';
    const OPTION_MODULES_DISABLED      = 'modules.disabled';
    const OPTION_DEFAULT_MODULE        = 'default.module';
    const OPTION_DEFAULT_CONTROLLER    = 'default.controller';
    const OPTION_DEFAULT_ACTION        = 'default.action';
    const OPTION_STATIC_MODULE         = 'static.module';
    const OPTION_STATIC_CONTROLLER     = 'static.controller';
    const OPTION_STATIC_ACTION         = 'static.action';

    /**
     * Konstruktor
     *
     * @param array $options parametry
     */
    public function __construct (array $options = array())
    {
        if(!empty($options)) {
            $this->_options = $options;
        }
    }

    /**
     * Definiowanie opcji
     *
     * @param string|array $option nazwa opcji lub tablica opcji
     * @param null|string $value wartość opcji w przypadku podania nazwy dla pierwszego argumentu
     * @return void
     */
    public function setOption ($option, $value = null)
    {
        if(is_array($option)) {
            $this->_options = array_merge($this->_options, $option);
        } else {
            $this->_options[$option] = $value;
        }
    }

    /**
     * Zwraca wartość opcji
     *
     * @param string|array $option nazwa opcji lub tablica opcji
     * @param mixed $default domyślna wartość
     * @return mixed
     */
    public function getOption ($option, $default = null)
    {
        if(isset($this->_options[$option])) {
            return $this->_options[$option];
        }

        if(isset($this->_defaults[$option])) {
            return $this->_defaults[$option];
        }

        return $default;
    }

    /**
     * Realizacja żądania
     *
     * @param \pl\nweb\framework\Request $request obiekt żądania
     * @param \pl\nweb\framework\Response $response obiekt odpowiedzi
     * @return void
     */
    public function dispatch (\pl\nweb\framework\Request $request, \pl\nweb\framework\Response $response)
    {
        $request->setDispatched(true);
        $namespace = $this->getOption(self::OPTION_APPLICATION_NAMESPACE);

        $controller = $this->getOption(self::OPTION_STATIC_CONTROLLER);
        if($controller && !$request->isForwarded()) {
            $request->setController($controller);
        } else if(!$request->hasController()) {
            $controller = $this->getOption(self::OPTION_DEFAULT_CONTROLLER);
            $request->setController($controller);
        } else {
            $controller = $request->getController();
        }

        $action = $this->getOption(self::OPTION_STATIC_ACTION);
        if($action && !$request->isForwarded()) {
            $request->setAction($action);
        } else if(!$request->hasAction()) {
            $action = $this->getOption(self::OPTION_DEFAULT_ACTION);
            $request->setAction($action);
        } else {
            $action = $request->getAction();
        }

        if($this->getOption(self::OPTION_MODULES_DISABLED)) {
            $class = '\\' . $namespace . '\\controllers\\' . ucfirst($controller);
        } else {
            $module = $this->getOption(self::OPTION_STATIC_MODULE);
            if($module && !$request->isForwarded()) {
                $request->setModule($module);
            } else if(!$request->hasModule()) {
                $module = $this->getOption(self::OPTION_DEFAULT_MODULE);
                $request->setModule($module);
            } else {
                $module = $request->getModule();
            }
            $class = '\\' . $namespace . '\\modules\\' . $module . '\\controllers\\' . ucfirst($controller);
        }

        if(!class_exists($class, true)) {
            throw new dispatcher\Exception ('Controller "' . $controller . '" not found in module "' . $module . '", class: ' . $class);
        }

        $obj = new $class ();
        $obj->dispatch($request, $response);
    }
}